﻿using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace InstantSpine.Core
{
    /// <summary>
    /// Extension methods to avoid some of the boilerplate code necessary when
    /// using the INotifyPropertyChanged interface for WPF data binding
    /// </summary>
    public static class NotifyPropertyChangedEx
    {
        /// <summary>
        /// Invokes the attached event handlers with the name of the changed
        /// property and its owner (sender) deduced from a given property-access AST
        /// </summary>
        /// <typeparam name="T">Type of the property that has changed</typeparam>
        /// <param name="pceh">The event to be raised</param>
        /// <param name="propertyAccessExpression">A lambda expression of the form <code>() => this.PropertyName</code></param>
        public static void Raise<T>(this PropertyChangedEventHandler pceh, Expression<Func<T>> propertyAccessExpression) {
            var accessExpr = propertyAccessExpression.Body as MemberExpression;
            if (accessExpr == null)
                throw new ArgumentException("The body of propertyAccessExpression must be a MemberExpression.", "propertyAccessExpression");
            
            var sender = ((ConstantExpression)accessExpr.Expression).Value;

            var propExpression = accessExpr.Member as PropertyInfo;
            if (propExpression == null)
                throw new ArgumentException("The body of propertyAccessExpression must be a property access expression.", "propertyAccessExpression");

            var propName = propExpression.Name;

            if (pceh != null)
                pceh.Invoke(sender, new PropertyChangedEventArgs(propName));
        }

        /// <summary>
        /// Invokes the attached event handlers with the name of the changed
        /// property provided by the CallerMemberName attribute
        /// </summary>
        /// <param name="pceh">The event to be raised</param>
        /// <param name="propertyName">The name of the changed property; this parameter is determined automatically and MUST NOT be set!</param>        
        public static void Raise(this PropertyChangedEventHandler pceh, object sender, [CallerMemberName] string propertyName = "") {
            if (pceh != null)
                pceh(sender, new PropertyChangedEventArgs(propertyName));
        }
    }
}
